﻿using System;
using Microsoft.Kinect;
using Schuifpuzzel.Model;

namespace Schuifpuzzel.MyKinect
{
    public enum Hand
    {
        LeftHand,
        RightHand
    }

    public delegate void GestureDetectedHandler(MoveDirection detectedMovement);

    public delegate void ArmStretchedDetected(Hand hand);

    public delegate void ArmPositionDetected(
        bool handIsInFrontOfBody, double xCoordinate, double yCoordinate, Boolean detectingGesture);

    /// <summary>
    /// This class is responsible for detecting when a gesture is made by the user
    /// </summary>
    public class GestureDetector
    {
        private const int DistanceForArmToBeStretched = 400;
        private const int DistanceForArmToBeInfrontOfBody = 200;
        public event GestureDetectedHandler GestureDetected;
        public event ArmStretchedDetected ArmStretched;
        public event ArmPositionDetected ArmPositionDetected;

        private Hand _trackedHand = Hand.RightHand;
        private Boolean _detectingSwipe = true;
        public static int ScaledDistanceToBeInStart = 10;
        public static int ScaledDistanceToSwipe = 10;

        private double xZeroPosition;
        private double yZeroPosition;

        /// <summary>
        /// The hand that is being tracked
        /// </summary>
        public Hand TrackedHand
        {
            get { return _trackedHand; }
            set { _trackedHand = value; }
        }

        /// <summary>
        /// This method takes a skeleton and raises an event to inform when the user has made a gesture
        /// </summary>
        /// <param name="skeleton">The current skeleton of the user</param>
        /// <param name="depth">The frame containing the depth data</param>
        public void DetectGesture(Skeleton skeleton, DepthImageFrame depth)
        {
            Boolean rightHandTracked = skeleton.Joints[JointType.HandRight].TrackingState ==
                                       JointTrackingState.Tracked;
            Boolean rightShoulderTracked = skeleton.Joints[JointType.ShoulderRight].TrackingState ==
                                           JointTrackingState.Tracked;

            Boolean leftHandTracked = skeleton.Joints[JointType.HandLeft].TrackingState == JointTrackingState.Tracked;
            Boolean leftShoulderTracked = skeleton.Joints[JointType.ShoulderLeft].TrackingState ==
                                          JointTrackingState.Tracked;

            //Check if right hand is stretched
            if (rightHandTracked && rightShoulderTracked)
            {
                DepthImagePoint rightHandDepthPoint =
                    depth.MapFromSkeletonPoint(skeleton.Joints[JointType.HandRight].Position);
                DepthImagePoint rightShoulderDepthPoint =
                    depth.MapFromSkeletonPoint(skeleton.Joints[JointType.ShoulderRight].Position);

                if (rightHandDepthPoint.Depth < (rightShoulderDepthPoint.Depth - DistanceForArmToBeStretched))
                {
                    if (ArmStretched != null)
                    {
                        ArmStretched(Hand.RightHand);
                    }
                }

            }

            //Check if left hand is stretched
            if (leftHandTracked && leftShoulderTracked)
            {
                DepthImagePoint leftHandDepthPoint =
                    depth.MapFromSkeletonPoint(skeleton.Joints[JointType.HandLeft].Position);
                DepthImagePoint leftShoulderDepthPoint =
                    depth.MapFromSkeletonPoint(skeleton.Joints[JointType.ShoulderLeft].Position);


                if (leftHandDepthPoint.Depth < (leftShoulderDepthPoint.Depth - DistanceForArmToBeStretched))
                {
                    if (ArmStretched != null)
                    {
                        ArmStretched(Hand.LeftHand);
                    }

                }
            }

            Boolean handTracked;
            Boolean shoulderTracked;
            DepthImagePoint handDepthPoint;
            DepthImagePoint shoulderDepthPoint;
            bool armIsInFrontOfBody = false;
            DepthImagePoint spineDepthPoint = depth.MapFromSkeletonPoint(skeleton.Joints[JointType.Spine].Position);

            bool shoulderCenterTracked = skeleton.Joints[JointType.ShoulderCenter].TrackingState == JointTrackingState.Tracked;
            bool spineTracked = skeleton.Joints[JointType.Spine].TrackingState == JointTrackingState.Tracked;

            //Use the depthpoints from the right side if tracking right hand
            if (_trackedHand == Hand.RightHand)
            {
                handTracked = rightHandTracked;
                shoulderTracked = rightShoulderTracked;
                handDepthPoint = depth.MapFromSkeletonPoint(skeleton.Joints[JointType.HandRight].Position);
                shoulderDepthPoint = depth.MapFromSkeletonPoint(skeleton.Joints[JointType.ShoulderRight].Position);
            }
            else
            {
                //Use the depthpoints from the left side if tracking left hand
                handTracked = rightHandTracked;
                shoulderTracked = leftShoulderTracked;
                handDepthPoint = depth.MapFromSkeletonPoint(skeleton.Joints[JointType.HandLeft].Position);
                shoulderDepthPoint = depth.MapFromSkeletonPoint(skeleton.Joints[JointType.ShoulderLeft].Position);
            }

            //Check if the tracked hand is in front of body
            //if (handTracked && shoulderTracked)
            //{
            //    if (handDepthPoint.Depth < (shoulderDepthPoint.Depth - DistanceForArmToBeInfrontOfBody))
            //    {
            //        armIsInFrontOfBody = true;

            //    }
            //    else
            //    {
            //        _detectingSwipe = false;
            //        if (ArmPositionDetected != null)
            //        {
            //            ArmPositionDetected(false, 0, 0, _detectingSwipe);
            //        }
            //    }

            //}

            if (handTracked && shoulderTracked && spineTracked && shoulderCenterTracked)
            {
                DepthImagePoint shoulderCenterDepthPoint = depth.MapFromSkeletonPoint(skeleton.Joints[JointType.ShoulderCenter].Position);

                xZeroPosition = shoulderDepthPoint.X;
                yZeroPosition = (spineDepthPoint.Y - shoulderCenterDepthPoint.Y ) / 3 * 2 + shoulderCenterDepthPoint.Y;

                double xHandPosition = handDepthPoint.X - xZeroPosition;
                double yHandPosition = yZeroPosition - handDepthPoint.Y;

                double xScaledPosition = xHandPosition / 5;
                double yScaledPosition = yHandPosition / 5;

                //Calculate the distance from the hand to the zero position by using Pythagoras                
                double distanceFromZero = Math.Sqrt(Math.Pow(xScaledPosition, 2) + Math.Pow(yScaledPosition, 2)); 

                //Call event to pass the x and y position
                if (ArmPositionDetected != null)
                {

                    ArmPositionDetected(true, xScaledPosition, yScaledPosition, _detectingSwipe);
                }


                //Check if arm is in startposition
                if (distanceFromZero < ScaledDistanceToBeInStart)
                {
                    //Player is now allowed to make a swipe
                    _detectingSwipe = true;
                }
                else if (_detectingSwipe)
                {
                    //Check if hand is far enough from zero position to have made a swipe
                    if (distanceFromZero > ScaledDistanceToSwipe)
                    {
                        Boolean xHigherThanY = Math.Abs(xScaledPosition) > Math.Abs((yScaledPosition));
                        if (xHigherThanY)
                        {
                            if (xHandPosition > 0)
                            {
                                //The user swiped right
                                _detectingSwipe = false;
                                if (GestureDetected != null)
                                    GestureDetected(MoveDirection.Right);
                            }
                            else
                            {
                                //The user swiped right left
                                _detectingSwipe = false;
                                if (GestureDetected != null)
                                    GestureDetected(MoveDirection.Left);
                            }

                        }
                        else
                        {
                            if (yHandPosition > 0)
                            {
                                //The user swiped right up
                                _detectingSwipe = false;
                                if (GestureDetected != null)
                                    GestureDetected(MoveDirection.Up);
                            }
                            else
                            {
                                //The user swiped right down
                                _detectingSwipe = false;
                                if (GestureDetected != null)
                                    GestureDetected(MoveDirection.Down);
                            }
                        }
                    }
                }
            }else
            {
                ArmPositionDetected(false, 0, 0, _detectingSwipe);
            }

        }
    }
}
